package shardkv

import (
	"time"
)
import "6.824/raft"

func (kv *ShardKV) pullConfig() {
	for !kv.Killed() {
		if term, isLeader := kv.rf.GetState(); isLeader {
			kv.mu.RLock()
			//当前配置版本
			curNum := kv.curConfig.Num
			kv.logger.Printf("pullConfig: 作为领导者（任期=%d），尝试拉取新配置（当前配置版本: %d）", term, curNum)
			//上一个版本的配置信息
			prevConfig := kv.curConfig

			//当前分片的所有信息是否都已经拉取完毕，并且删除完毕，才能拉取下一个版本的配置
			canPull := true
			for sid, _ := range kv.curConfig.Shards {
				if kv.Shards[sid].State != Ready {
					kv.logger.Printf("pullConfig: 分片%d当前状态为%d（非Ready），不允许拉取新配置", sid, kv.Shards[sid].State)
					canPull = false
					break
				}
			}
			kv.mu.RUnlock()
			if canPull {
				kv.logger.Printf("pullConfig: 所有分片均处于Ready状态，允许拉取新配置（当前版本: %d）", curNum)
				newConfig := kv.mck.Query(curNum + 1)

				if curNum >= newConfig.Num {
					kv.logger.Printf("pullConfig: 未获取到更新的配置（当前版本: %d，查询到版本: %d）", curNum, newConfig.Num)
				} else {
					//第一次拉取配置且新配置版本号大于1，代表需要拉取分片，需要查询获得上一个版本的配置信息
					if curNum == 0 && newConfig.Num > 1 {
						kv.logger.Printf("pullConfig: 首次拉取高版本配置（新版本: %d），同步获取上一版本配置（版本: %d）", newConfig.Num, newConfig.Num-1)
						prevConfig = kv.mck.Query(newConfig.Num - 1)
					}
					kv.logger.Printf("pullConfig: 发现新配置（当前版本: %d，新版本: %d），开始提交配置更新", curNum, newConfig.Num)
					command := ConfigCommand{prevConfig, newConfig}
					index, _, _ := kv.rf.Start(command)
					kv.logger.Printf("pullConfig: 配置更新命令已提交（Raft索引: %d，新配置版本: %d）", index, newConfig.Num)
				}
			}
		}
		time.Sleep(PullConfigTimeout)
	}
}

func (kv *ShardKV) applyConfig(msg *raft.ApplyMsg) {
	command := msg.Command.(ConfigCommand)
	prevConfig, newConfig := command.PrevConfig, command.CurConfig

	kv.logger.Printf("applyConfig: 开始应用配置（Raft索引: %d，待应用版本: %d，当前配置版本: %d）", msg.CommandIndex, newConfig.Num, kv.curConfig.Num)

	if newConfig.Num == kv.curConfig.Num+1 {
		kv.curConfig = newConfig
		kv.prevConfig = prevConfig
		kv.logger.Printf("applyConfig: 配置应用成功（旧版本: %d → 新版本: %d）", prevConfig.Num, newConfig.Num)

		//需要接受和发送分片,新配置版本号为1时，上个版本为初始配置，此时不需要拉取
		if newConfig.Num > 1 {
			for shardId, gid := range kv.prevConfig.Shards {
				//分片上个版本中不属于当前组，但现在属于，需要去别人那拉取
				if gid != kv.gid && kv.curConfig.Shards[shardId] == kv.gid {
					kv.logger.Printf("applyConfig: 分片%d需要从组%d迁移到当前组，状态设为NeedPull", shardId, gid)
					kv.Shards[shardId].State = NeedPull
				}
				//以前是现在不是，需要发送给别人
				if gid == kv.gid && kv.curConfig.Shards[shardId] != kv.gid {
					kv.logger.Printf("applyConfig: 分片%d需要从当前组迁移到组%d，状态设为Waiting", shardId, newConfig.Shards[shardId])
					kv.Shards[shardId].State = Waiting
				}
				// 一直不是的和一直是的不需要额外处理
			}
		} else {
			kv.logger.Printf("applyConfig: 应用初始配置（版本: %d），无需处理分片迁移", newConfig.Num)
		}

	} else {
		kv.logger.Printf("applyConfig: 配置应用跳过（版本不连续，待应用: %d，当前: %d）", newConfig.Num, kv.curConfig.Num)
	}
}
